﻿Imports System.Collections
Imports System.Collections.Generic
Imports System.IO
Imports System.Net
Imports System.Text
Imports System.Web
Imports System.Net.Security
Imports System.Security.Authentication
Imports System.Security.Cryptography.X509Certificates
Imports Pop.Api.Request

Namespace Pop.Api.Util
    ''' <summary>
    ''' 网络工具类。
    ''' </summary>
    Public NotInheritable Class WebUtils

        Private Const Boundary As String = "--------------------------------ThIs_Is_tHe_bouNdaRY_$"

        ''' <summary>
        ''' 请求与响应的超时时间
        ''' </summary>
        Property Timeout As Integer = 100000

        ''' <summary>
        ''' 执行HTTP POST请求。
        ''' </summary>
        ''' <param name="url">请求地址</param>
        ''' <param name="parameters">请求参数</param>
        ''' <param name="sign">鉴权参数sign</param>
        ''' <returns>HTTP响应</returns>
        Public Function DoPost(ByVal url As String, ByVal parameters As PopDictionary, ByVal sign As String) As String
            '确定编码
            Dim encod As Encoding
            encod = If(url.Contains("?charset=utf-8"), Encoding.UTF8, Encoding.GetEncoding("gbk"))

            '创建HttpWebRequest
            Dim req As HttpWebRequest = GetWebRequest(url, "POST")
            req.Headers.Add("sign:" & sign)
            req.ContentType = "multipart/form-data; boundary=" & Boundary

            '要发送的数据
            Dim postData As Byte() = encod.GetBytes(parameters.BuildQuery)
            Dim reqStream As Stream = req.GetRequestStream()
            reqStream.Write(postData, 0, postData.Length)
            reqStream.Close()

            Dim rsp As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
            Return GetResponseAsString(rsp, encod)
        End Function

        ''' <summary>
        ''' 执行HTTP GET请求。
        ''' </summary>
        ''' <param name="url">请求地址</param>
        ''' <param name="parameters">请求参数</param>
        ''' <param name="sign">鉴权参数sign</param>
        ''' <returns>HTTP响应</returns>
        Public Function DoGet(ByVal url As String, ByVal parameters As PopDictionary, ByVal sign As String) As String
            Dim encod As Encoding
            encod = If(url.Contains("?charset=utf-8"), Encoding.UTF8, Encoding.GetEncoding("gbk"))
            parameters.Add("sign", sign)
            If parameters IsNot Nothing AndAlso parameters.Count > 0 Then
                If url.Contains("?") Then
                    url &= "&" & parameters.NewBuildQuery
                Else
                    url &= "?" & parameters.NewBuildQuery
                End If
            End If

            Dim req As HttpWebRequest = GetWebRequest(url, "GET")
            req.ContentType = "multipart/form-data; boundary=" & Boundary

            Dim rsp As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
            Return GetResponseAsString(rsp, encod)
        End Function

        ''' <summary>
        ''' 执行带文件上传的HTTP POST请求。
        ''' </summary>
        ''' <param name="url">请求地址</param>
        ''' <param name="textParams">请求文本参数</param>
        ''' <param name="fileParams">请求文件参数</param>
        ''' <returns>HTTP响应</returns>
        Public Function DoPost(ByVal url As String, ByVal textParams As PopDictionary, ByVal fileParams As IDictionary(Of String, FileItem)) As String
            ' 如果没有文件参数，则走普通POST请求
            If fileParams Is Nothing OrElse fileParams.Count = 0 Then
                Return DoPost(url, textParams, "sign")
            End If

            Dim boundary As String = DateTime.Now.Ticks.ToString("X")
            ' 随机分隔线
            Dim req As HttpWebRequest = GetWebRequest(url, "POST")
            req.ContentType = "multipart/form-data;charset=utf-8;boundary=" & boundary

            Dim reqStream As System.IO.Stream = req.GetRequestStream()
            Dim itemBoundaryBytes As Byte() = Encoding.UTF8.GetBytes(vbCr & vbLf & "--" & boundary & vbCr & vbLf)
            Dim endBoundaryBytes As Byte() = Encoding.UTF8.GetBytes(vbCr & vbLf & "--" & boundary & "--" & vbCr & vbLf)

            ' 组装文本请求参数
            Dim textTemplate As String = "Content-Disposition:form-data;name=""{0}""" & vbCr & vbLf & "Content-Type:text/plain" & vbCr & vbLf & vbCr & vbLf & "{1}"
            Dim textEnum As IEnumerator(Of KeyValuePair(Of String, String)) = textParams.GetEnumerator()
            While textEnum.MoveNext()
                Dim textEntry As String = String.Format(textTemplate, textEnum.Current.Key, textEnum.Current.Value)
                Dim itemBytes As Byte() = Encoding.UTF8.GetBytes(textEntry)
                reqStream.Write(itemBoundaryBytes, 0, itemBoundaryBytes.Length)
                reqStream.Write(itemBytes, 0, itemBytes.Length)
            End While

            ' 组装文件请求参数
            Dim fileTemplate As String = "Content-Disposition:form-data;name=""{0}"";filename=""{1}""" & vbCr & vbLf & "Content-Type:{2}" & vbCr & vbLf & vbCr & vbLf
            Dim fileEnum As IEnumerator(Of KeyValuePair(Of String, FileItem)) = fileParams.GetEnumerator()
            While fileEnum.MoveNext()
                Dim key As String = fileEnum.Current.Key
                Dim fileItem As FileItem = fileEnum.Current.Value
                Dim fileEntry As String = String.Format(fileTemplate, key, fileItem.GetFileName(), fileItem.GetMimeType())
                Dim itemBytes As Byte() = Encoding.UTF8.GetBytes(fileEntry)
                reqStream.Write(itemBoundaryBytes, 0, itemBoundaryBytes.Length)
                reqStream.Write(itemBytes, 0, itemBytes.Length)

                Dim fileBytes As Byte() = fileItem.GetContent()
                reqStream.Write(fileBytes, 0, fileBytes.Length)
            End While

            reqStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length)
            reqStream.Close()

            Dim rsp As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
            Dim encod As Encoding = Encoding.GetEncoding(rsp.CharacterSet)
            Return GetResponseAsString(rsp, encod)
        End Function

        Public Function CheckValidationResult(ByVal sender As Object, ByVal certificate As X509Certificate, ByVal chain As X509Chain, ByVal errors As SslPolicyErrors) As Boolean
            '直接确认，否则打不开
            Return True
        End Function

        Public Function GetWebRequest(ByVal url As String, ByVal method As String) As HttpWebRequest
            Dim req As HttpWebRequest = Nothing
            If url.Contains("https") Then
                ServicePointManager.ServerCertificateValidationCallback = New RemoteCertificateValidationCallback(AddressOf CheckValidationResult)
                req = DirectCast(WebRequest.CreateDefault(New Uri(url)), HttpWebRequest)
            Else
                req = DirectCast(WebRequest.Create(url), HttpWebRequest)
            End If

            req.ServicePoint.Expect100Continue = False
            req.Method = method
            req.KeepAlive = True
            req.UserAgent = "Top4Net"
            req.Timeout = Me._Timeout

            Return req
        End Function

        ''' <summary>
        ''' 把响应流转换为文本。
        ''' </summary>
        ''' <param name="rsp">响应流对象</param>
        ''' <param name="encoding">编码方式</param>
        ''' <returns>响应文本</returns>
        Public Function GetResponseAsString(ByVal rsp As HttpWebResponse, ByVal encoding As Encoding) As String
            Dim stream As System.IO.Stream = Nothing
            Dim reader As StreamReader = Nothing

            Try
                ' 以字符流的方式读取HTTP响应
                stream = rsp.GetResponseStream()
                reader = New StreamReader(stream, encoding)
                Return reader.ReadToEnd()
            Finally
                ' 释放资源
                If reader IsNot Nothing Then
                    reader.Close()
                End If
                If stream IsNot Nothing Then
                    stream.Close()
                End If
                If rsp IsNot Nothing Then
                    rsp.Close()
                End If
            End Try
        End Function

        ''' <summary>
        ''' 组装GET请求URL。
        ''' </summary>
        ''' <param name="url">请求地址</param>
        ''' <param name="parameters">请求参数</param>
        ''' <returns>带参数的GET请求URL</returns>
        Public Function BuildGetUrl(ByVal url As String, ByVal parameters As IEnumerable(Of KeyValuePair(Of String, String))) As String
            Dim encod As Encoding
            encod = If(url.Contains("?charset=utf-8"), Encoding.UTF8, Encoding.GetEncoding("gbk"))
            If parameters IsNot Nothing AndAlso parameters.Count > 0 Then
                If url.Contains("?") Then
                    url &= "&" & BuildQuery(parameters, encod)
                Else
                    url &= "?" & BuildQuery(parameters, encod)
                End If
            End If
            Return url
        End Function

        ''' <summary>
        ''' 组装普通文本请求参数。
        ''' </summary>
        ''' <param name="parameters">Key-Value形式请求参数字典</param>
        ''' <returns>URL编码后的请求数据</returns>
        Public Shared Function BuildQuery(ByVal parameters As IEnumerable(Of KeyValuePair(Of String, String)), ByVal encod As Encoding) As String
            Dim postData As New StringBuilder()
            Dim hasParam As Boolean = False

            Dim dem As IEnumerator(Of KeyValuePair(Of String, String)) = parameters.GetEnumerator()
            While dem.MoveNext()
                Dim name As String = dem.Current.Key
                Dim value As String = dem.Current.Value
                ' 忽略参数名或参数值为空的参数
                If Not String.IsNullOrEmpty(name) AndAlso Not String.IsNullOrEmpty(value) Then
                    If hasParam Then
                        postData.Append("&")
                    End If

                    postData.Append(name)
                    postData.Append("=")
                    postData.Append(value)
                    'postData.Append(HttpUtility.UrlEncode(value, encod))
                    hasParam = True
                End If
            End While

            Return postData.ToString()
        End Function

#Region "invokeApi"

        Friend Shared Function invokeApi(ByVal Url As String, ByVal parameters As List(Of FormField), ByVal SecretKey As String) As String
            Try
                Dim data As List(Of Byte) = Util.TopUtils.MultipartData(parameters)
                'request对象
                Dim request As HttpWebRequest = DirectCast(WebRequest.Create(Url), HttpWebRequest)
                request.Method = "POST"
                request.ServicePoint.Expect100Continue = False
                request.KeepAlive = False
                request.Headers.Add("sign:" & Util.TopUtils.GetSign(Url, parameters, SecretKey))
                request.ContentType = "multipart/form-data; boundary=" & Boundary
                request.ContentLength = data.Count
                request.GetRequestStream.Write(data.ToArray, 0, data.Count)
                request.GetRequestStream.Close()
                Dim Body As String, encod As Encoding
                encod = If(Url.IndexOf("?charset=utf-8") <> -1, Encoding.UTF8, Encoding.GetEncoding("GB2312"))
                Using response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
                    Using sr As New StreamReader(response.GetResponseStream, encod)
                        Body = sr.ReadToEnd
                        sr.Close()
                    End Using
                End Using
                Return Body
            Catch ex As Exception
                Throw New PopException("-1", "HttpWebRequest连接失败!")
            End Try
        End Function

        Friend Shared Function invokeApi(Of T As PopResponse)(ByVal req As IPopRequest(Of T), ByVal parameters As PopDictionary, ByVal SecretKey As String) As String
            Try
                Dim data As Byte() = Encoding.UTF8.GetBytes(parameters.BuildQuery)
                Dim url As String = req.ServerUrl
                'request对象
                Dim request As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
                request.Method = "POST"
                request.ServicePoint.Expect100Continue = False
                request.KeepAlive = False
                request.Headers.Add("sign:" & parameters.GetDictionary.GetSign(req.GetApiName, SecretKey))
                request.ContentType = "multipart/form-data; boundary=" & Boundary
                request.ContentLength = data.Count
                request.GetRequestStream.Write(data.ToArray, 0, data.Count)
                request.GetRequestStream.Close()

                Dim Body As String, encod As Encoding
                encod = If(url.IndexOf("?charset=utf-8") <> -1, Encoding.UTF8, Encoding.GetEncoding("GB2312"))
                Using response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
                    Using sr As New StreamReader(response.GetResponseStream, encod)
                        Body = sr.ReadToEnd
                        sr.Close()
                    End Using
                End Using
                Return Body
            Catch ex As Exception
                Throw New PopException("-1", "HttpWebRequest连接失败!")
            End Try
        End Function

#End Region

    End Class

End Namespace